﻿using System;
using System.Linq;
using System.Management;
using ClusterAware.Properties;
using ClusterAwareProperties;
using ClusterAwareWMI;

namespace ClusterAware
{
    /// <summary>
    /// Wrapper property to Group Class actions
    /// </summary>
    public class GroupMethods
    {
        #region private properties

        private readonly ClusterManager _managerclass;

        #endregion

        #region Ctor

        /// <summary>
        /// private Constructor
        /// </summary>
        private GroupMethods()
        {
        }

        /// <summary>
        /// Create Class GroupMethods
        /// </summary>
        /// <param name="managerclass">Instance of ClusterManager class</param>
        public GroupMethods(ClusterManager managerclass)
            : this()
        {
            _managerclass = managerclass;
        }

        #endregion

        #region public Methods

        /// <summary>
        /// CusterManager Instance
        /// </summary>
        public ClusterManager ManagerInstance
        {
            get { return _managerclass; }
        }

        /// <summary>
        /// if Exists Group
        /// </summary>
        /// <param name="groupName">Group name</param>
        /// <returns>True if exists</returns>
        ///<exception cref="ClusterManagerException">
        /// this method throws exception for the following conditions:
        /// Not Connected, 
        /// Parameter group name is empty
        /// </exception>
        public bool ExistGroup(string groupName)
        {
            if (!_managerclass.IsConnected)
            {
                throw new ClusterManagerException(Resources.Error_NotConnected);
            }
            if (string.IsNullOrEmpty(groupName))
            {
                throw new ClusterManagerException(string.Format(Resources.Error_ParamEmpty, "groupName"));
            }
            if (_managerclass.CacheInstances)
            {
                return _managerclass.GroupInstances.ContainsKey(groupName);
            }
            try
            {
                var grp = new ClusterWmiHelpper().GetGroup(groupName, _managerclass.ScopeManager);
                return (grp != null);
            }
            catch
            {
                return false;
            }
        }

        /// <summary>
        /// get list of Group 
        /// </summary>
        /// <returns>Array of class Group</returns>
        ///<exception cref="ClusterManagerException">
        /// this method throws exception for the following conditions:
        /// Not Connected
        /// </exception>
        public GroupProperty[] GetGroups()
        {
            if (!_managerclass.IsConnected)
            {
                throw new ClusterManagerException(Resources.Error_NotConnected);
            }
            try
            {
                return _managerclass.CacheInstances ? _managerclass.GroupInstances.Values.ToArray() : new ClusterWmiHelpper().GetGroups(_managerclass.ScopeManager);
            }
            catch (ManagementException ex)
            {
                throw new ClusterManagerException(string.Format(Resources.Error_MethodExecute, "Groups.GetGroups", ex.Message), ex);
            }
            catch (Exception ex)
            {
                throw new ClusterManagerException(string.Format(Resources.Error_MethodExecute, "Groups.GetGroups", ex.Message), ex);
            }
        }

        /// <summary>
        /// Get a Group
        /// </summary>
        /// <param name="groupName">name group</param>
        /// <returns>Group class</returns>
        ///<exception cref="ClusterManagerException">
        /// this method throws exception for the following conditions:
        /// Not Connected, 
        /// Parameter group name is empty, 
        /// Group not exists
        /// </exception>
        public GroupProperty GetGroup(string groupName)
        {
            if (!_managerclass.IsConnected)
            {
                throw new ClusterManagerException(Resources.Error_NotConnected);
            }
            if (string.IsNullOrEmpty(groupName))
            {
                throw new ClusterManagerException(string.Format(Resources.Error_ParamEmpty, "groupName"));
            }
            if (_managerclass.CacheInstances)
            {
                if (!_managerclass.GroupInstances.ContainsKey(groupName))
                {
                    throw new ClusterManagerException(string.Format(Resources.Error_ParamNotExist, "Group", groupName));
                }
                return _managerclass.GroupInstances[groupName];
            }
            try
            {
                return new ClusterWmiHelpper().GetGroup(groupName, _managerclass.ScopeManager);
            }
            catch (ManagementException ex)
            {
                throw new ClusterManagerException(string.Format(Resources.Error_MethodExecute, "Groups.GetGroup", ex.Message), ex);
            }
            catch (Exception ex)
            {
                throw new ClusterManagerException(string.Format(Resources.Error_MethodExecute, "Groups.GetGroup", ex.Message), ex);
            }
        }

        /// <summary>
        /// Brings a group online.
        /// </summary>
        /// <param name="groupName">Grpoup name</param>
        /// <param name="segtimeout">The length of time (in seconds) that the method should wait for the resource to come online.</param>
        /// <remarks>
        /// If property CacheInstances of CusterManager is true all resoures in cache are refreshed
        /// </remarks>
        ///<exception cref="ClusterManagerException">
        /// this method throws exception for the following conditions:
        /// Not Connected, 
        /// Parameter group name is empty, 
        /// Group not exists, 
        /// Failed WMI method execution
        /// </exception>
        /// 
        public void BringOnline(string groupName, UInt32 segtimeout)
        {
            if (!_managerclass.IsConnected)
            {
                throw new ClusterManagerException(Resources.Error_NotConnected);
            }
            if (string.IsNullOrEmpty(groupName))
            {
                throw new ClusterManagerException(string.Format(Resources.Error_ParamEmpty, "groupName"));
            }
            if (!ExistGroup(groupName))
            {
                throw new ClusterManagerException(string.Format(Resources.Error_ParamNotExist, "Group", groupName));
            }
            try
            {
                var group = _managerclass.Groups.GetGroup(groupName);
                using (var inParams = group.Instance.GetMethodParameters("BringOnline"))
                {
                    inParams["TimeOut"] = segtimeout;
                    group.Instance.InvokeMethod("BringOnline", inParams, null);
                    if (_managerclass.CacheInstances)
                    {
                        _managerclass.Refresh(RefreshOption.Groups, groupName);
                        _managerclass.Refresh(RefreshOption.Resources);
                    }
                }
            }
            catch (ManagementException ex)
            {
                throw new ClusterManagerException(string.Format(Resources.Error_MethodExecute, "Groups.BringOnline", ex.Message), ex);
            }
            catch (Exception ex)
            {
                throw new ClusterManagerException(string.Format(Resources.Error_MethodExecute, "Groups.BringOnline", ex.Message), ex);
            }
        }

        /// <summary>
        /// Take a group offline.
        /// </summary>
        /// <param name="groupName">Group name</param>
        /// <param name="segtimeout">The length of time (in seconds) that the method should wait for the resource to go offline.</param>
        /// <remarks>
        /// If property CacheInstances of CusterManager is true all resoures in cache are refreshed
        /// </remarks>
        ///<exception cref="ClusterManagerException">
        /// this method throws exception for the following conditions:
        /// Not Connected, 
        /// Parameter group name is empty, 
        /// Group not exists, 
        /// Failed WMI method execution
        /// </exception>
        public void TakeOffline(string groupName, UInt32 segtimeout)
        {
            if (!_managerclass.IsConnected)
            {
                throw new ClusterManagerException(Resources.Error_NotConnected);
            }
            if (string.IsNullOrEmpty(groupName))
            {
                throw new ClusterManagerException(string.Format(Resources.Error_ParamEmpty, "groupName"));
            }
            if (!ExistGroup(groupName))
            {
                throw new ClusterManagerException(string.Format(Resources.Error_ParamNotExist, "Group", groupName));
            }
            try
            {
                var group = _managerclass.Groups.GetGroup(groupName);
                using (var inParams = group.Instance.GetMethodParameters("TakeOffline"))
                {
                    inParams["TimeOut"] = segtimeout;
                    group.Instance.InvokeMethod("TakeOffline", inParams, null);
                    if (_managerclass.CacheInstances)
                    {
                        _managerclass.Refresh(RefreshOption.Groups, groupName);
                        _managerclass.Refresh(RefreshOption.Resources);
                    }
                }
            }
            catch (ManagementException ex)
            {
                throw new ClusterManagerException(string.Format(Resources.Error_MethodExecute, "Groups.TakeOffline", ex.Message), ex);
            }
            catch (Exception ex)
            {
                throw new ClusterManagerException(string.Format(Resources.Error_MethodExecute, "Groups.TakeOffline", ex.Message), ex);
            }
        }


        /// <summary>
        /// Move a Group to a other Node 
        /// </summary>
        /// <param name="groupName">Name group</param>
        /// <param name="nodeName">Name Node</param>
        /// <param name="timeout">Ammout seconds to timeout operation</param>
        /// <remarks>
        /// If property CacheInstances of CusterManager is true all resoures in cache are refreshed
        /// </remarks>
        ///<exception cref="ClusterManagerException">
        /// this method throws exception for the following conditions:
        /// Not Connected, 
        /// Parameter group name is empty, 
        /// Parameter group name not exists, 
        /// Parameter Node name is empty, 
        /// Node not exists, 
        /// Failed WMI method execution
        /// </exception>
        public void MoveToNewNode(string groupName, string nodeName, UInt32 timeout)
        {
            if (!_managerclass.IsConnected)
            {
                throw new ClusterManagerException(Resources.Error_NotConnected);
            }
            if (string.IsNullOrEmpty(groupName))
            {
                throw new ClusterManagerException(string.Format(Resources.Error_ParamEmpty, "groupName"));
            }
            if (!ExistGroup(groupName))
            {
                throw new ClusterManagerException(string.Format(Resources.Error_ParamNotExist, "Group", groupName));
            }
            if (string.IsNullOrEmpty(nodeName))
            {
                throw new ClusterManagerException(string.Format(Resources.Error_ParamEmpty, "nodeName"));
            }
            if (!_managerclass.Nodes.ExistNode(nodeName))
            {
                throw new ClusterManagerException(string.Format(Resources.Error_ParamNotExist, "Node", nodeName));
            }
            try
            {
                var group = _managerclass.Groups.GetGroup(groupName);
                using (var inParams = group.Instance.GetMethodParameters("MoveToNewNode"))
                {
                    inParams["NodeName"] = nodeName;
                    inParams["TimeOut"] = timeout;
                    group.Instance.InvokeMethod("MoveToNewNode", inParams, null);
                    if (_managerclass.CacheInstances)
                    {
                        _managerclass.Refresh(RefreshOption.Groups, groupName);
                        _managerclass.Refresh(RefreshOption.Resources);
                    }
                }
            }
            catch (ManagementException ex)
            {
                throw new ClusterManagerException(string.Format(Resources.Error_MethodExecute, "Groups.MoveToNewNode", ex.Message), ex);
            }
            catch (Exception ex)
            {
                throw new ClusterManagerException(string.Format(Resources.Error_MethodExecute, "Groups.MoveToNewNode", ex.Message), ex);
            }
        }


        ///<summary>
        /// Save a Group in Cluster
        ///</summary>
        ///<param name="groupClass">Group CLass to Save</param>
        ///<returns>ClusterProperties Class</returns>
        ///<exception cref="ClusterManagerException">
        /// this method throws exception for the following conditions:
        /// Not Connected, 
        /// Failed WMI method execution
        /// </exception>
        public GroupProperty Save(GroupProperty groupClass)
        {
            if (!_managerclass.IsConnected)
            {
                throw new ClusterManagerException(Resources.Error_NotConnected);
            }
            if (groupClass == null)
            {
                throw new ClusterManagerException(string.Format(Resources.Error_ParamEmpty, "groupclass"));
            }
            if (!ExistGroup(groupClass.Name))
            {
                throw new ClusterManagerException(string.Format(Resources.Error_ParamNotExist, "Group", groupClass.Name));
            }
            try
            {
                groupClass.SaveManagementObject(_managerclass.OperationSystem);
                _managerclass.Refresh(RefreshOption.Groups, groupClass.Name);
                return _managerclass.Groups.GetGroup(groupClass.Name);
            }
            catch (ManagementException ex)
            {
                throw new ClusterManagerException(string.Format(Resources.Error_MethodExecute, "Groups.Save", ex.Message), ex);
            }
            catch (Exception ex)
            {
                throw new ClusterManagerException(string.Format(Resources.Error_MethodExecute, "Groups.Save", ex.Message), ex);
            }
        }

        /// <summary>
        /// Get list Resource of a group
        /// </summary>
        /// <param name="groupName">name Group</param>
        /// <returns>Array Resource class</returns>
        /// <exception cref="ClusterManagerException">
        ///  this method throws exception for the following conditions:
        ///  Not Connected, 
        ///  Parameter group name is empty, 
        ///  Group not exists
        ///  </exception>
        /// <remarks>Not Cachebled result</remarks>
        public ResourceProperty[] GetResources(string groupName)
        {
            if (!_managerclass.IsConnected)
            {
                throw new ClusterManagerException(Resources.Error_NotConnected);
            }
            if (string.IsNullOrEmpty(groupName))
            {
                throw new ClusterManagerException(string.Format(Resources.Error_ParamEmpty, "groupName"));
            }
            try
            {
                return new ClusterWmiHelpper().FindResourcesByGroup(groupName, _managerclass.ScopeManager);
            }
            catch (Exception ex)
            {
                throw new ClusterManagerException(string.Format(Resources.Error_MethodExecute, "Groups.GetResources", ex.Message), ex);
            }
        }

        /// <summary>
        /// Get a active node of a group
        /// </summary>
        /// <param name="groupName">name Group</param>
        /// <returns>node class</returns>
        /// <exception cref="ClusterManagerException">
        ///  this method throws exception for the following conditions:
        ///  Not Connected, 
        ///  Parameter group name is empty, 
        ///  Group not exists
        ///  </exception>
        /// <remarks>Not Cachebled result</remarks>
        public NodeProperty GetActiveNode(string groupName)
        {
            if (!_managerclass.IsConnected)
            {
                throw new ClusterManagerException(Resources.Error_NotConnected);
            }
            if (string.IsNullOrEmpty(groupName))
            {
                throw new ClusterManagerException(string.Format(Resources.Error_ParamEmpty, "groupName"));
            }
            try
            {
                return new ClusterWmiHelpper().FindActiveNodeByGroup(groupName, _managerclass.ScopeManager);
            }
            catch (Exception ex)
            {
                throw new ClusterManagerException(string.Format(Resources.Error_MethodExecute, "Groups.GetActiveNode", ex.Message), ex);
            }
        }
        
        /// <summary>
        /// Get list Preferred nodes of a group
        /// </summary>
        /// <param name="groupName">name Group</param>
        /// <returns>Array node class</returns>
        /// <exception cref="ClusterManagerException">
        ///  this method throws exception for the following conditions:
        ///  Not Connected, 
        ///  Parameter group name is empty, 
        ///  Group not exists
        ///  </exception>
        /// <remarks>Not Cachebled result</remarks>
        public NodeProperty[] GetPreferredNodes(string groupName)
        {
            if (!_managerclass.IsConnected)
            {
                throw new ClusterManagerException(Resources.Error_NotConnected);
            }
            if (string.IsNullOrEmpty(groupName))
            {
                throw new ClusterManagerException(string.Format(Resources.Error_ParamEmpty, "groupName"));
            }
            try
            {
                return new ClusterWmiHelpper().FindPreferredNodeByGroup(groupName, _managerclass.ScopeManager);
            }
            catch (Exception ex)
            {
                throw new ClusterManagerException(string.Format(Resources.Error_MethodExecute, "Groups.GetPreferredNodes", ex.Message), ex);
            }
        }

        #endregion
    }
}
